// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml { 
	import actionml.core.LoadingUtils;
	import actionml.display.*;
	import actionml.interaction.*;
	
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.events.ProgressEvent;
	import flash.utils.setTimeout;
	
	import flash.display.LoaderInfo;

// content files, accordion button, global tweens with override, mouse_leave on main doc 
	public dynamic class ActionMLDocument extends MovieClip {
		protected var _content:XML;
		protected var _interactions:XML;
		protected var _preloader:DisplayObject;		
		protected var _curtain:DisplayObject;	
		public function ActionMLDocument() {
			if (this.stage) {
				this.load();
			}
		}
		public function getCurtain():DisplayObject {
			return _curtain;
		}	
		public function get content() {
			return _content;
		}
		public function set content(xml:XML) {
			_content = xml;
		}
		public function get interactions() {
			return _interactions;
		}
		public function set interactions(xml:XML) {
			_interactions = xml;
		}	
		public function load() {
			this.stop();
			Global.initialize(this);
			_preloader = this.showPreloader("preloader");
			for each (var c:Object in [BasicInteractions, ButtonInteractions, MediaInteractions, PopupInteractions, DisplayObjectMethods, DisplayObjectAccessorMethods, DragAndDropInteractions, MovieClipMethods, PlayTween, SliderInteractions, TextInteractions, TimelineController, VideoInteractions]) {
				c.initialize();
			}
			if (!_preloader) {
				_preloader = this.showPreloader("preloader");
			}
			if ("dependencies" in this.constructor && this.constructor.dependencies is Array) {
				for each (c in this.constructor.dependencies) {
					c.initialize();
				}
			}
			this.stage.stageFocusRect = false;
			var loaded:Boolean = false;
			var ineractionXmlLoaded:Boolean = false;
			var contentXmlLoaded:Boolean = false;
			var timeoutExpired:Boolean = false;
			var initialized:Boolean = false;
			var minimumPreloadDuration:Number = 1000;
			var minimumPreloadPercentage:Number = .8;
			if ("minimumPreloadDuration" in this.constructor && (this.constructor.minimumPreloadDuration || this.constructor.minimumPreloadDuration == 0)) {
				minimumPreloadDuration = this.constructor.minimumPreloadDuration * 1000;
			}
			if ("minimumPreloadPercentage" in this.constructor && (this.constructor.minimumPreloadPercentage || this.constructor.minimumPreloadPercentage == 0)) {
				minimumPreloadPercentage = this.constructor.minimumPreloadPercentage;
			}
			setTimeout(function() {
				if (initialized) {
					start();
				}
				timeoutExpired = true;				
			}, minimumPreloadDuration);
			var o:MovieClip = this;
			if (this.loaderInfo.bytesLoaded >= this.loaderInfo.bytesTotal) {
				loaded = true;
			} else {
				this.loaderInfo.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent) {
					if (e.bytesLoaded / e.bytesTotal > minimumPreloadPercentage && o.framesLoaded == o.totalFrames) {
						if (ineractionXmlLoaded && contentXmlLoaded) {
							doInitialize();
						}
						loaded = true;
					}
				});
			}
			if (!_interactions) {
				LoadingUtils.requestURL(this.loaderInfo.url["replaceExtension"]("xml"), { completeHandler: function(interactions:Object) {
					_interactions = XML(interactions);
					if (_interactions.hasOwnProperty("content") && _interactions.content.@file) {
						loadContent(_interactions.content.@file);
					} else {
						contentXmlLoaded = true;
						if (loaded) {
							doInitialize();	
						}
					}
					ineractionXmlLoaded = true;
				}});
			} else {
				this.doEvent("loadingComplete");
			}
			function loadContent(fileName:String) {
				LoadingUtils.requestURL(fileName, { completeHandler: function(content:Object) {
					_content = XML(content);
					if (loaded) {
						doInitialize();
					}
					contentXmlLoaded = true;
				}});			
			}
			function doInitialize() {
				o.doEvent("loadingComplete");
				initialize();
				if (timeoutExpired) {
					start();
				}
				initialized = true;
			}
		}		
		public function initialize() {	
			var t:Object = ActionMLDocument["getTracer"]({method:"initialize", context:"xml"});
			if (_interactions.hasOwnProperty("preload") && _interactions.preload.@instanceName) {
				if (_preloader) {
					if (_interactions.preload.@instanceName != "preload") {
						t.warn("This piece contains the default preloader with an instance name of preload and preloader called", _interactions.preload.@instanceName, ".  This is an error as only one preloader should be used.  Only the default preloader will be displayed.");
					}
				} else {
					_preloader = this.showPreloader(_interactions.preload.@instanceName);
				}
			}
			if (_interactions.hasOwnProperty("backgroundLayer")) {
				for each (var bgLayer:XML in XMLList(_interactions.backgroundLayer)) {
					this.addChild(MovieClip["fromXML"](bgLayer, true, this));
				}
			}
			for each (var xml:XML in _interactions.elements()) {
				if (xml.name().localName == "trace") {
					var appender:Class = Class["forName"]("actionml.tracing.Appender");
					if (appender) {
						for each (var appenderXml:XML in xml.elements()) {
							appender["addAppenderFromXml"](appenderXml);
						}
					} else {
						trace("Your XML file includes trace configuration but the trace classes are not present in the swf.")
					}
				} else if (xml.name().localName == "actions") {
					// include attribute actions
					var actionAttributes:Object = Object(xml).getAttributes();
					for (var attribute:String in actionAttributes) {
						var actions:Array = actionAttributes[attribute].split(/\s*,\s*/);
						Global["actions"] ||= [];
						Global["actions"][attribute] = function(o:Object, event:String) {
							for each (var a:String in actions) {
								this.doAction(a, event);
							}
						}
					}
					// include element actions
					for each (var actionXml:XML in xml.elements()) {
						var statements:Array = [];
						for each (var statementXml:XML in Object(actionXml).elements()) {
							var localName:String = statementXml.localName();
							var attributes:Object = Object(statementXml).getAttributes();
							if (localName == "do") {
								statements.push(attributes["actions"].split(/\s*,\s*/));
							} else if (localName == "assign") {
								statements.push(attributes);
							} else if (localName == "tween") {
								statements.push(o["loadTweenFromXml"](statementXml));
							} else {
								trace("Invalid action XML at " + statementXml.toXMLString());
							}
						}
						// the action is stored as a method using the
						// name of the action element as the method name
						// the method loops through the statements array
						// and executes action statements, plays tweens, 
						// and sets values for expressions
						Global["actions"] ||= [];
						Global["actions"][Object(actionXml).localName()] = function(o:Object, event:String) {
							for each (var s:Object in statements) {
								if (s is Array) {
									for each (var a:String in s) {
										this.doAction(a, event);
									}
								} else if (s.constructor.toString().endsWith("Tween")) {
									this.playTween(s);
								} else {
									for (var p:String in s) {
										this.setValueForKeypath(p, this.evaluateExpression(s[p]));
									}
								}
							}
						};
					}
				} else if (xml.name().localName == "tweens") {
					for each (var tweenXml:XML in xml.elements()) {
						var tweenAttributes:Object = Object(tweenXml).getAttributes();
						tweenAttributes.name = tweenXml.localName();
						Global["tweens"] ||= [];
						Global["tweens"][tweenAttributes.name] = tweenAttributes;
					}
				} else if (xml.name().localName == "layer") {
					var layerClip:MovieClip = MovieClip["fromXML"](xml, true, this);
					this.initializeLayer(layerClip, xml);
					this.addChild(layerClip);
				} else if (xml.name().localName != "backgroundLayer" && xml.name().localName != "content") { // background layers were already handled
					var o:Object = this.initializeElement(xml);
				}
			}
			// curtain init must come last so it is moved to the front of all other elements
			_curtain = this.getChildByName("curtain");
			if (_interactions.hasOwnProperty("curtain") && _interactions.curtain.@instanceName) {
				if (_curtain) {
					if (_interactions.curtain.@instanceName != "curtain") {
						t.warn("This piece contains the default curtain with an instance name of curtain and curtain called", _interactions.curtain.@instanceName, ".  This is an error as only one curtain should be used.  Only the default curtain will be used.");
					}
				} else {
					_curtain = this.getChildByName(_interactions.curtain.@instanceName);
					_curtain["configureFromXML"](_interactions.curtain);
				}
			}
			if (_curtain) {
				if (isNaN(_curtain["showingAlpha"])) {
					_curtain["showingAlpha"] = _curtain.alpha;
				}
				_curtain["moveToFront"]();	
				_curtain.mask = null;
				_curtain["mouseEnabled"] = false;
				_curtain["mouseChildren"] = false;
				_curtain.x = 0;
				_curtain.y = 0;
				if (this.parent == this.stage) {
					_curtain.width = this.stage.stageWidth;
					_curtain.height = this.stage.stageHeight;
				} else {
					_curtain.width = this.width;
					_curtain.height = this.height;
				}
				_curtain.visible = false;
			}
		}
		protected function prepareToPlay() {
			
		}
		protected function startPlaying() {

		}
		protected function initializeLayer(layerClip:MovieClip, layerXml:XML) {
			
		}
		protected function initializeElement(elementXml:XML) {
			return MovieClip["fromXML"](elementXml, true, this);
		}
		public function start() {
			this.prepareToPlay();
			this["walkTree"](function(descendent:DisplayObject) {
				//if ("initTweens" in descendent || "onInit" in descendent || ("tweens" in descendent && descendent["tweens"] && descendent["tweens"]["onInit"])) {
					descendent["doEvent"]("init");
				//}
			});
			if (_preloader) {
				var o:DisplayObject = this;
				_preloader["playTween"]({alpha:0, duration:2}).callWhenFinished(function() {
					if (DisplayObjectMethods.instanceMethods.hasChild.call(o.stage, _preloader)) {
						o.stage.removeChild(_preloader);
					}
					o["walkTree"](function(descendent:DisplayObject) {
						if ("introTweens" in descendent || "onIntro" in descendent || ("tweens" in descendent && descendent["tweens"] && descendent["tweens"]["onIntro"])) {
							descendent["doEvent"]("intro");
						}
					});
					o["startPlaying"]();
				});
			} else {
				this.walkTree(function(descendent:DisplayObject) {
					if ("introTweens" in descendent || "onIntro" in descendent || ("tweens" in descendent && descendent["tweens"] && descendent["tweens"]["onIntro"])) {
						descendent["doEvent"]("intro");
					}
				});
				this.startPlaying();
			}
		}
		private function showPreloader(name:String) {
			var preloader:DisplayObject = this.getChildByName(name);
			if (!preloader && Class["forName"] is Function) {
				var c:Class = Class["forName"]("Preloader");
				if (c) {
					preloader = new c();
				}
			}
			if (preloader) {
				if (!DisplayObjectMethods.instanceMethods.hasChild.call(this.stage, preloader)) {
					this.stage.addChild(preloader);
				}
				preloader.mask = null;
				if (this.parent == this.stage) {
					preloader.x = 0;
					preloader.y = 0;
					preloader.width = this.stage.stageWidth;
					preloader.height = this.stage.stageHeight;
				} else {
					preloader.x = this.parent.x;
					preloader.y = this.parent.y;	
					preloader.width = this.width;
					preloader.height = this.height;
				}
				preloader["mouseEnabled"] = false;
				preloader["mouseChildren"] = false;
				if ("play" in preloader && preloader["play"] is Function) {
					preloader["play"]();
				}
			}
			return preloader;
		}
	}
}